import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

class XinDongVideoPlayerProgressIndicator extends StatefulWidget {
  final VideoPlayerController controller;
  final VideoProgressColors colors;
  final double radius;
  final bool allowSeek;
  final Function(Duration position) seekTo;

  const XinDongVideoPlayerProgressIndicator(
      this.controller, {
        Key? key,
        this.colors = const VideoProgressColors(),
        this.radius = 5.0,
        this.allowSeek = false,
        required this.seekTo,
      }) : super(key: key);

  @override
  State<XinDongVideoPlayerProgressIndicator> createState() => _XinDongVideoPlayerProgressIndicatorState();
}

class _XinDongVideoPlayerProgressIndicatorState extends State<XinDongVideoPlayerProgressIndicator> {
  _XinDongVideoPlayerProgressIndicatorState() {
    listener = () {
      if (!mounted) {
        return;
      }
      setState(() {});
    };
  }

  late VoidCallback listener;

  VideoPlayerController get controller => widget.controller;

  VideoProgressColors get colors => widget.colors;

  @override
  void initState() {
    super.initState();
    controller.addListener(listener);
  }

  @override
  void deactivate() {
    controller.removeListener(listener);
    super.deactivate();
  }

  @override
  Widget build(BuildContext context) {
    Widget progressIndicator;
    if (controller.value.isInitialized) {
      final int duration = controller.value.duration.inMilliseconds;
      final int position = controller.value.position.inMilliseconds;

      int maxBuffering = 0;
      for (final DurationRange range in controller.value.buffered) {
        final int end = range.end.inMilliseconds;
        if (end > maxBuffering) {
          maxBuffering = end;
        }
      }

      progressIndicator = Stack(
        fit: StackFit.passthrough,
        children: <Widget>[
          LinearProgressIndicator(
            value: maxBuffering / duration,
            valueColor: AlwaysStoppedAnimation<Color>(colors.bufferedColor),
            backgroundColor: colors.backgroundColor,
          ),
          LinearProgressIndicator(
            value: position / duration,
            valueColor: AlwaysStoppedAnimation<Color>(colors.playedColor),
            backgroundColor: Colors.transparent,
          ),
        ],
      );
    } else {
      progressIndicator = LinearProgressIndicator(
        valueColor: AlwaysStoppedAnimation<Color>(colors.playedColor),
        backgroundColor: colors.backgroundColor,
      );
    }
    final radiusProgressIndicator = ClipRRect(
      borderRadius: BorderRadius.circular(widget.radius),
      child: progressIndicator,
    );
    if (widget.allowSeek) {
      return _XinDongVideoScrubber(
        seekTo: widget.seekTo,
        controller: controller,
        child: Padding(
          padding: const EdgeInsets.only(top: 16.0, bottom: 16.0),
          child: radiusProgressIndicator,
        ),
      );
    } else {
      return radiusProgressIndicator;
    }
  }
}

class _XinDongVideoScrubber extends StatefulWidget {
  final Widget child;
  final VideoPlayerController controller;
  final Function(Duration position) seekTo;

  const _XinDongVideoScrubber({required this.child, required this.controller, required this.seekTo});

  @override
  _XinDongVideoScrubberState createState() => _XinDongVideoScrubberState();
}

class _XinDongVideoScrubberState extends State<_XinDongVideoScrubber> {
  bool _controllerWasPlaying = false;

  VideoPlayerController get controller => widget.controller;

  @override
  Widget build(BuildContext context) {
    void seekToRelativePosition(Offset globalPosition) {
      final RenderBox box = context.findRenderObject()! as RenderBox;
      final Offset tapPos = box.globalToLocal(globalPosition);
      final double relative = tapPos.dx / box.size.width;
      final Duration position = controller.value.duration * relative;
      controller.seekTo(position);
      widget.seekTo(position);
    }

    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      child: widget.child,
      onHorizontalDragStart: (DragStartDetails details) {
        if (!controller.value.isInitialized) {
          return;
        }
        _controllerWasPlaying = controller.value.isPlaying;
        if (_controllerWasPlaying) {
          controller.pause();
        }
      },
      onHorizontalDragUpdate: (DragUpdateDetails details) {
        if (!controller.value.isInitialized) {
          return;
        }
        seekToRelativePosition(details.globalPosition);
      },
      onHorizontalDragEnd: (DragEndDetails details) {
        if (controller.value.position != controller.value.duration) {
          if (_controllerWasPlaying) {
            controller.play();
          }
          widget.seekTo(controller.value.position);
        }
      },
      onTapDown: (TapDownDetails details) {
        if (!controller.value.isInitialized) {
          return;
        }
        seekToRelativePosition(details.globalPosition);
      },
    );
  }
}
